type partie =
  {
    date: float;
    encheres: int array;
    noms: string array;
    preneur: int;
    appele: int;
    carte_appelee: Carte.carte option;
    chien: Carte.carte array option;
    ecart: Carte.carte array option;
    plis: (int * Carte.carte * int) list array;
    dp_gagnes: int array;
    bouts_remportes: int array;
    statut_pab: [`Attaque | `Defense] option;
  } 

let egal partie1 partie2 =
  let tab_egaux egal_elt t1 t2 = 
    let n1 = Array.length t1
    and n2 = Array.length t2 in
    let rec aux i =
      if i < n1 && i < n2 then
	(egal_elt t1.(i) t2.(i)) && aux (i + 1)
      else i = n1 && i = n2
    in
    aux 0
  in
  let chaines_egales str1 str2 =
    let n1 = String.length str1 
    and n2 = String.length str2 in
    let rec aux i =
      if i < n1 && i < n2 then
	(str1.[i] = str2.[i]) && aux (i + 1)
      else i = n1 && i = n2
    in
    aux 0
  in
  let options_egales egal_elt o1 o2 =
    match (o1, o2) with
    | (None, None) -> true
    | (Some o1, Some o2) -> egal_elt o1 o2
    | _ -> false
  in
  let encheres = tab_egaux (=) partie1.encheres partie2.encheres in
  let noms = tab_egaux (chaines_egales) partie1.noms partie2.noms in
  let preneur = partie1.preneur = partie2.preneur in
  let appele = partie1.appele = partie2.appele in
  let carte_appelee = partie1.carte_appelee = partie2.carte_appelee in
  let chien = options_egales (tab_egaux (=)) partie1.chien partie2.chien in
  let ecart = options_egales (tab_egaux (=)) partie1.ecart partie2.ecart in
  let plis = tab_egaux (=) partie1.plis partie2.plis in
  let dp_gagnes = tab_egaux (=) partie1.dp_gagnes partie2.dp_gagnes in
  let bouts_remportes = tab_egaux (=) partie1.bouts_remportes partie2.bouts_remportes in
  let statut_pab = partie1.statut_pab = partie2.statut_pab in
  encheres && noms && preneur && appele && carte_appelee && chien && ecart
  && plis && dp_gagnes && bouts_remportes && statut_pab

let remove_double =
  let rec remove_double_aux selected = function
    | [] -> List.rev selected
    | a :: b when not (List.exists ((=) a) b) -> 
       remove_double_aux (a :: selected) b
    | _ :: b -> remove_double_aux selected b
  in
  remove_double_aux []

type tas_carte = bool array

let union_tas liste_tas =
  let rec carte_presente i = function
    | [] -> false
    | tab :: _ when tab.(i) -> true
    | _ :: reste -> carte_presente i reste
  in
  let c_p liste i = carte_presente i liste in
  Array.init 78 (c_p liste_tas)

let inversion_tas = Array.map (not)

let intersection_tas liste_tas =
  let rec carte_presente i = function
    | [] -> true
    | tab :: _ when not (tab.(i)) -> false
    | _ :: reste -> carte_presente i reste
  in
  let c_p liste i = carte_presente i liste in
  Array.init 78 (c_p liste_tas)

let map2 f t1 t2 =
  Array.init (min (Array.length t1) (Array.length t2))
    (fun i -> f t1.(i) t2.(i))

let difference_tas = map2 (fun a b -> a && not b)

let tas_complet () = Array.make 78 true

let tas_vide () = Array.make 78 false

let tas_of_liste tas =
  let rec aux t = function
    | [] -> t
    | a :: b ->
       let () = t.(a) <- true in
       aux t b
  in
  aux (Array.make 78 false) tas

let (liste_of_tas_rev: bool array -> int list) =
  let rec aux (i: int) (liste: int list) (t: bool array) : int list =
    if i < Array.length t && t.(i) then
      aux (i + 1) (i :: liste) t
    else if i < Array.length t then 
      aux (i + 1) liste t
    else liste
  in
  aux 0 []

let liste_of_tas t = List.rev (liste_of_tas_rev t)

let desoptionnaliser = function
  | None -> failwith "Impossible..."
  | Some x -> x

let of_single_tarotv t =
  let date = Unix.time () in
  let encheres =
    Array.map (function None -> -1
		      | Some prise -> prise)
	      t.Tarotv.encheres in
  let noms = Array.copy t.Tarotv.mes_adversaires in
  let preneur = 
    match t.Tarotv.preneur with None -> -1 | Some x -> x in
  let carte_appelee = t.Tarotv.carte_appelee in
  let appele = match t.Tarotv.joueur_appele with 
    | None -> -1
    | Some appele -> appele
  in
  let chien = 
    match t.Tarotv.chien with
    | [a ; b ; c] -> Some [|a ; b ; c|]
    | _ -> None
  in
  let ecart =
    match t.Tarotv.ecart_maximum with
    | [a ; b ; c] -> Some [|a ; b ; c|]
    | _ -> None
  in
  let plis = t.Tarotv.cartes_par_pli in
  let dp_gagnes =
    let tmp = Array.make 5 (-1) in
    let () =
      match t.Tarotv.mon_numero with
      | Some i -> tmp.(i) <- t.Tarotv.dp_gagnes 
      | None -> ()
    in
    tmp
  in
  let bouts_remportes =
    let tmp = Array.make 5 (-1) in
    let () =
      match t.Tarotv.mon_numero with
      | Some i -> tmp.(i) <- t.Tarotv.bouts_remportes
      | None -> ()
    in
    tmp
  in
  let statut_pab = t.Tarotv.petit_au_bout in
  {date = date ; 
   encheres = encheres ; noms = noms ; preneur = preneur ; appele = appele ;
   carte_appelee = carte_appelee ; chien = chien ; ecart = ecart ; 
   plis = plis ; dp_gagnes = dp_gagnes ; 
   bouts_remportes = bouts_remportes ; statut_pab = statut_pab}

let of_tarotv t =
  let date = Unix.time () in
  let encheres = Array.init 5 
    (fun i -> match t.(i).Tarotv.encheres.(i) with None -> -1
    | Some x -> x) in
  let noms = Array.init 5 (fun i -> 
    t.(i).Tarotv.mon_nom) in
  let preneur = 
    match t.(0).Tarotv.preneur with None -> -1 | Some x -> x in
  let carte_appelee = t.(0).Tarotv.carte_appelee in
  let appele = match carte_appelee, preneur with 
    | None, _
    | _, -1 -> -1
    | _, preneur -> (* si personne ne sait qu'il est appelé, c'est le preneur *)
       let selon = Array.map (fun etat -> etat.Tarotv.joueur_appele) t in
       let rec trouver_appele i =
	 if i < Array.length selon && selon.(i) = None then
	   trouver_appele (i + 1)
	 else if i < Array.length selon then 
	   desoptionnaliser selon.(i)
	 else preneur
       in
       trouver_appele 0
  in
  let chien = 
    match t.(0).Tarotv.chien with
    | [a ; b ; c] -> Some [|a ; b ; c|]
    | _ ->
	(* Le chien n'est pas connu *)
       let cartes = Array.map (fun t -> t.Tarotv.mon_jeu) t in
       let cartes = Array.map (List.map (Carte.int_of_carte)) cartes in
       let tas = List.map (tas_of_liste) (Array.to_list cartes) in
       let cartes = liste_of_tas (difference_tas (tas_complet ()) (union_tas tas)) in
       let cartes = List.map (Carte.carte_of_int) cartes in
       Some (Array.of_list cartes)
  in
  let ecart =
    let selon = Array.to_list
		  (Array.map
		     (fun etat ->
		      let c = etat.Tarotv.ecart_maximum in
		      (* let () = Printf.printf *)
		      (* 		 "Selon %s, l'écart maximum est %s.\n" *)
		      (* 		 etat.Tarotv.mon_nom *)
		      (* 		 (String.concat *)
		      (* 		    ", " *)
		      (* 		    (List.map (Carte.string_of_carte) *)
		      (* 			      c)) in *)
		      c) t) in
    let selon = List.map (List.map (Carte.int_of_carte)) selon in
    (* let () = List.iter *)
    (* 	       (fun cartes -> *)
    (* 		let () = print_string "[" in *)
    (* 		let () = *)
    (* 		  List.iter *)
    (* 		    (fun i -> *)
    (* 		     print_int i ; *)
    (* 		     print_string "; ") *)
    (* 		    cartes *)
    (* 		in *)
    (* 		print_endline "]") *)
    (* 	       selon in *)
    let tas_selon = List.map (tas_of_liste) selon in
    (* let () = List.iter *)
    (* 	       (fun tas -> *)
    (* 		let () =  *)
    (* 		  Array.iter *)
    (* 		    (function true -> print_int 1 *)
    (* 			    | false -> print_int 0) *)
    (* 		    tas *)
    (* 		in *)
    (* 		print_newline ()) *)
    (* 	       tas_selon in *)
    let cartes = liste_of_tas (intersection_tas tas_selon) in
    match List.map (Carte.carte_of_int) cartes with
    | [a ; b ; c] ->
       (* let () = Printf.printf "L'écart peut être déterminé.\n" in *)
       Some [|a ; b ; c|]
    | liste ->
       (* let () = Printf.printf "L'écart est contenu dans l'ensemble suivant : %s\n" *)
       (* 			      (String.concat ", " *)
       (* 					     (List.map (Carte.string_of_carte) *)
       (* 						       liste)) in *)
       None
  in
  let plis = t.(0).Tarotv.cartes_par_pli in
  let (dp_gagnes, bouts_remportes) = 
    (Array.map (fun t -> t.Tarotv.dp_gagnes) t,
     Array.map (fun t -> t.Tarotv.bouts_remportes) t)
  in
  let statut_pab = t.(0).Tarotv.petit_au_bout in
  {date = date ; 
   encheres = encheres ; noms = noms ; preneur = preneur ; appele = appele ;
   carte_appelee = carte_appelee ; chien = chien ; ecart = ecart ; 
   plis = plis ; dp_gagnes = dp_gagnes ; 
   bouts_remportes = bouts_remportes ; statut_pab = statut_pab}
    
let to_html partie =
  let open Eliom_content.Html5.F in
  let classes_joueurs = 
    let t = Array.make 5 [] in
    if partie.preneur >= 0 then t.(partie.preneur) <- "preneur" :: t.(partie.preneur) ;
    if partie.appele >= 0 then t.(partie.appele) <- "appele" :: t.(partie.appele) ;
    t
  in
  let liste_joueurs =
    let tab = Array.mapi (fun i e -> (i, e))
      partie.noms in
    ol ~a:[a_class ["joueurs"]]
      (List.rev 
	 (Array.fold_left
	    (fun liste (i, nom) ->
	      (li ~a:[a_class classes_joueurs.(i)]
		 [pcdata nom]) :: liste)
	    [] (tab)))
  in
  let liste_encheres =
    let tab = Array.mapi (fun i e -> (i, e)) 
      partie.encheres in
    ol ~a:[a_class ["encheres"]]
      (List.rev 
	 (Array.fold_left
	    (fun liste (i, e) ->
	      (li ~a:[a_class [match e with 
	      | 0 -> "passe"
	      | 1 -> "petite"
	      | 2 -> "garde"
	      | 3 -> "gardesans"
	      | 4 -> "gardecontre"
	      | _ -> "inconnu"]]
		 [pcdata (partie.noms.(i)^" "^(match e with
		 | 0 -> "passe"
		 | 1 -> "fait une petite"
		 | 2 -> "garde"
		 | 3 -> "garde sans le chien"
		 | 4 -> "garde contre le chien"
		 | _ -> "n'a rien dit")^".")]) :: liste)
	    [] tab))
  in
  let image_carte t c =
    img ~alt:(t^" : "^(Carte.string_of_carte c))
      ~src:(make_uri
	      ~service:(Eliom_service.preapply
			  ~service:Service_carte.service 
			  (Carte.int_of_carte c))
	      ())()
  in
  let carte_appelee =
    match partie.carte_appelee with
    | None -> p [pcdata "Aucune carte n'a encore été appelée"]
    | Some c -> p [image_carte "Carte appelée" c]
  in
  let chien =
    ul ~a:[a_class ["chien"]]
      (List.rev
	 (Array.fold_left
	    (fun liste carte ->
	      (li [image_carte "Carte du chien" carte]) :: liste
	    )
	    []
	    (match partie.chien with Some chien -> chien
				   | None -> [||])
	 )
      )
  in
  let ecart = 
    ul ~a:[a_class ["ecart"]]
      (List.rev
	 (Array.fold_left
	    (fun liste carte ->
	      (li [image_carte "Carte écartée" carte]) :: liste
	    )
	    []
	    (match partie.ecart with Some ecart -> ecart
				   | None -> [||])
	 )
      )
  in
  let jeu =
    let carte (poseur, carte, gagnant) =
      tr [td [p ~a:[a_class classes_joueurs.(poseur)]
		 [pcdata partie.noms.(poseur)]] ;
	  td [image_carte "Carte jouée" carte] ;
	  td [p ~a:[a_class classes_joueurs.(gagnant)]
		 [pcdata partie.noms.(gagnant)]]]
    in
    let pli i liste_cartes_jouees =
      div ~a:[a_class ["pli"]]
	  ((h2 [pcdata ("Pli numéro "^(string_of_int (i + 1)))]) ::
	     (match (List.map (carte) liste_cartes_jouees) with
	      | a :: b -> [table a b]
	      | [] -> []))
    in
    let tab_plis = Array.mapi (pli) partie.plis in
    div ~a:[a_class ["jeu"]]
      (Array.to_list tab_plis)
  in
  let res =
    let dp_gagnes =
      let trs =
	let pile = ref [] in
	let () = Array.iteri
		   (fun i dp ->
		    if dp >= 0 then
		      pile := 
			(tr ~a:[a_class classes_joueurs.(i)]
			    [td [pcdata (partie.noms.(i))] ;
			     td [pcdata ((string_of_int (dp / 2))
					 ^(if dp mod 2 = 0 then "" else ",5"))]])
			:: (!pile)
		   )
		   partie.dp_gagnes in
	List.rev !pile
      in
      match trs with [] -> div []
		   | a :: b ->
		      table a b
    in
    let bouts_remportes =
      let trs =
	let pile = ref [] in
	let () = Array.iteri
		   (fun i b ->
		    if b >= 0 then
		      pile := 
			(tr ~a:[a_class classes_joueurs.(i)]
			    [td [pcdata (partie.noms.(i))] ;
			     td [pcdata (string_of_int b)]])
			:: (!pile)
		   )
		   partie.bouts_remportes in
	List.rev !pile
      in
      match trs with [] -> div []
		   | a :: b ->
		      table a b
    in
    let statut_pab =
      match partie.statut_pab with
      | None -> p ~a:[a_class ["pab"]]
	 [pcdata "Pas de petit au bout."]
      | Some `Attaque -> p ~a:[a_class ["pab"]]
	 [pcdata "Petit au bout pour l'attaque."]
      | Some `Defense -> p ~a:[a_class ["pab"]]
	 [pcdata "Petit au bout pour la défense."]
    in
    div [h2 [pcdata "Points de l'équipe (selon chacun)"] ; 
	 dp_gagnes ; 
	 h2 [pcdata "Bouts de l'équipe (selon chacun)"] ; 
	 bouts_remportes ; 
	 h2 [pcdata "Petit au bout"] ;
	 statut_pab]
  in
  div ~a:[a_class ["partie"]]
    [ div ~a:[a_class ["entete"]]
	[ h1 [pcdata "Participants"] ;
	  liste_joueurs ;
	  h1 [pcdata "Enchères"] ;
	  liste_encheres] ;
      div ~a:[a_class ["ecart"]] 
	[ h1 [pcdata "Carte appelée"] ;
	  carte_appelee];
      div ~a:[a_class ["chien"]]
	[ h1 [pcdata "Chien"] ;
	  chien ;
	  h1 [pcdata "Écart"] ; 
	  ecart] ;
      div ~a:[a_class ["jeu"]]
	[ h1 [pcdata "Jeu"] ;
	  jeu] ;
      div ~a:[a_class ["resultats"]]
	[ h1 [pcdata "Résultats"] ;
	  res]]
